/* DHT library 

MIT license
written by Adafruit Industries
*/

#include <math.h>
#include "DHT.h"
//#define NAN 0
#ifdef DEBUG
#define DEBUG_PRINT(...)  Serial.println(__VA_ARGS__)
#else
#define DEBUG_PRINT(...)
#endif
DHT::DHT(uint8_t pin, uint8_t type, uint8_t count)
    : _pin(pin), _type(type), _count(count), firstreading(true)
{
    
}



void DHT::begin(void) {

  if(_type == DHT10){
    if(DHT10Init()){
        SERIALPRINT.println("Error : Failed to init DHT 11\n");
		    while(1);
    }
  }
  else{
    // set up the pins!
    pinMode(_pin, INPUT);
    digitalWrite(_pin, HIGH);
    _lastreadtime = 0;
  }

}

/**Common  interface to get temp&humi value.support all DHT device.
 * 
 * @return 0 for calibrated failed,1 for succeed.
 **/
int DHT::readTempAndHumidity(float *data)
{
    uint32_t target_val[2] = {0};
    if(_type == DHT10){
        uint32_t cnt = 0;
      while(DHT10ReadStatus()==0)
      {
        DHT10Init();
        delay(30);
        cnt++;
        if(cnt > 3)
          return -1;
      }
        //wait for data ready。
        while(readTargetData(target_val)){
          cnt++;
          delay(50);
          if(cnt > 5){
            return -1;
          }

        }
        data[0] = target_val[0] *100.0/1024/1024;
        data[1] = target_val[1] *200.0/1024/1024-50;

    }
    else{
        data[0] = readHumidity();
        data[1] = readTemperature();
        if (isnan(data[0]) || isnan(data[1])){
            return -1;
        }
    }
    return 0;
}

//boolean S == Scale.  True == Farenheit; False == Celcius
float DHT::readTemperature(bool S) {

  if (read()) {
      float f;
    switch (_type) {
    case DHT11:
      f = data[2];
      if(S)
      	f = convertCtoF(f);
      	
      return f;
    case DHT22:
    case DHT21:
      f = data[2] & 0x7F;
      f *= 256;
      f += data[3];
      f /= 10;
      if (data[2] & 0x80)
	f *= -1;
      if(S)
	f = convertCtoF(f);

      return f;
    }
  }
  DEBUG_PRINT("Read fail");
  return NAN;
}

float DHT::convertCtoF(float c) {
	return c * 9 / 5 + 32;
}

float DHT::readHumidity(void) {
  if (read()) {
      float f;
    switch (_type) {
    case DHT11:
      f = data[0];
      return f;
    case DHT22:
    case DHT21:
      f = data[0];
      f *= 256;
      f += data[1];
      f /= 10;
      return f;
    }
  }
  DEBUG_PRINT("Read fail");
  return NAN;
}


boolean DHT::read(void) {
  uint8_t laststate = HIGH;
  // uint8_t counter = 0;
  uint8_t j = 0, i;
  unsigned long currenttime;

  // pull the pin high and wait 250 milliseconds
  digitalWrite(_pin, HIGH);
  delay(250);

  currenttime = millis();
  if (currenttime < _lastreadtime) {
    // ie there was a rollover
    _lastreadtime = 0;
  }
  if (!firstreading && ((currenttime - _lastreadtime) < 2000)) {
    return true; // return last correct measurement
    //delay(2000 - (currenttime - _lastreadtime));
  }
  firstreading = false;
  /*
    DEBUG_PRINT("Currtime: "); DEBUG_PRINT(currenttime);
    DEBUG_PRINT(" Lasttime: "); DEBUG_PRINT(_lastreadtime);
  */
  _lastreadtime = millis();

  data[0] = data[1] = data[2] = data[3] = data[4] = 0;
  
  // now pull it low for ~20 milliseconds
  pinMode(_pin, OUTPUT);
  digitalWrite(_pin, LOW);
  delay(20);
  //cli();
  digitalWrite(_pin, HIGH);
  delayMicroseconds(40);
  pinMode(_pin, INPUT);

  // read in timings
  for ( i=0; i< MAXTIMINGS; i++) {
    uint8_t counter = 0;
    while (digitalRead(_pin) == laststate) {
      counter++;
      delayMicroseconds(1);
      if (counter == 255) {
        break;
      }
    }
    laststate = digitalRead(_pin);

    if (counter == 255) break;

    // ignore first 3 transitions
    if ((i >= 4) && (i%2 == 0)) {
      // shove each bit into the storage bytes
      data[j/8] <<= 1;
      if (counter > _count)
        data[j/8] |= 1;
      j++;
    }

  }

  //sei();
  
  /*
  DEBUG_PRINTln(j, DEC);
  DEBUG_PRINT(data[0], HEX); DEBUG_PRINT(", ");
  DEBUG_PRINT(data[1], HEX); DEBUG_PRINT(", ");
  DEBUG_PRINT(data[2], HEX); DEBUG_PRINT(", ");
  DEBUG_PRINT(data[3], HEX); DEBUG_PRINT(", ");
  DEBUG_PRINT(data[4], HEX); DEBUG_PRINT(" =? ");
  DEBUG_PRINTln(data[0] + data[1] + data[2] + data[3], HEX);
  */

  // check we read 40 bits and that the checksum matches
  if ((j >= 40) && 
      (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) ) {
    return true;
  }
  

  return false;

}

/*****************************************************************************/
/*****************************************************************************/

/**Reset sensor.
 * @return 0 for calibrated failed,1 for succeed.
 **/
int DHT::DHT10Reset(void)
{
  if(_type == DHT10)
      return i2cWriteByte(RESET_REG_ADDR);
  else{
      SERIALPRINT.println("This function only support for DHT10");
      return 0;
  } 

}

/** Read status register.check the calibration flag - bit[3]: 1- calibrated ok ,0 - Not calibrated.
 * 
 * @return 0 for calibrated failed,1 for succeed. 
 * 
 **/
int DHT::DHT10ReadStatus(void)
{

	// int ret = 0;
  if(_type == DHT10){
	uint8_t statu = 0;
        int ret = i2cReadByte(statu);
        if(ret)
            SERIALPRINT.println("Failed to read byte\n");
        if((statu & 0x8)==0x8)
            return 1;
        else
            return 0;
  }
  else{
        SERIALPRINT.println("This function only support for DHT10");
        return 0;
  }
    
}

/** Init sensor,send 0x08,0x00 to register 0xe1.
 *  @ return : 0 if success, non-zero if failed.
 **/
int DHT::setSystemCfg(void)
{
  if(_type == DHT10)
  {
      uint8_t cfg_param[] = {0xe1,0x08,0x00};
      return i2cWriteBytes(cfg_param,sizeof(cfg_param));
  }
  else {
    SERIALPRINT.println("This function only support for DHT10");
    return 0;
  }
}


/** Read temp & humi result buf from sensor.
 *  total 6 bytes,the first byte for status register,other 5 bytes for temp & humidity data.
 *  @ return : 0 if success, non-zero if failed.
 **/
int DHT::readTargetData(uint32_t *data)
{
	/* int ret = 0; unused */

  if(_type == DHT10){
      uint8_t statu = 0;
	uint8_t bytes[6] = {0};
	uint8_t cfg_params[] ={0xac,0x33,0x00};

    if( i2cWriteBytes(cfg_params,sizeof(cfg_params)) ){
      return -1;
    }

    delay(75);
    // check device busy flag， bit[7]:1 for busy, 0 for idle.
    while(statu /* & (0x80 == 0x80) */ ){
      SERIALPRINT.println("Device busy!");
      delay(200);
      if(i2cReadByte(statu)){
        return -1;
      }	
    }

    if(i2cReadBytes(bytes,sizeof(bytes))){
      return -1;
    }
    

    data[HUMIDITY_INDEX] = (data[HUMIDITY_INDEX] | bytes[1]) << 8;
    data[HUMIDITY_INDEX] = (data[HUMIDITY_INDEX] | bytes[2]) << 8;
    data[HUMIDITY_INDEX] = (data[HUMIDITY_INDEX] | bytes[3]);
    data[HUMIDITY_INDEX] = data[HUMIDITY_INDEX] >>4;

    data[TEMPRATURE_INDEX] = (data[TEMPRATURE_INDEX] | bytes[3]) << 8;
    data[TEMPRATURE_INDEX] = (data[TEMPRATURE_INDEX] | bytes[4]) << 8;
    data[TEMPRATURE_INDEX] = (data[TEMPRATURE_INDEX] | bytes[5]);
    data[TEMPRATURE_INDEX] &= 0xfffff;

    return 0;
  }
  else {
    SERIALPRINT.println("This function only support for DHT10");
    return 0;
  }
}

/**DHT10 Init function.
 * Reset sensor and wait for calibration complete.
 * @ return : 0 if success, non-zero if failed.
 **/ 
int DHT::DHT10Init(void)
{
  if(_type == DHT10)
  {
      int ret = 0;
      int cnt = 0;

      delay(500);
      DHT10Reset();
      delay(300);
      
      ret = setSystemCfg();
      if(ret){
        SERIALPRINT.println("Failed to set system conf reg \n");
      }
      //SERIALPRINT.println("Set system cfg OK!");

      delay(500);
      
      while(DHT10ReadStatus()==0)
      {
        SERIALPRINT.println("get status error!");
        DHT10Reset();
        delay(500);
        if(setSystemCfg()){
          SERIALPRINT.println("Failed to set system conf reg \n");
        }
        delay(500);
        cnt++;
        if(cnt>5)
          return -1;
      }
      return 0;
  }
  else{
      SERIALPRINT.println("This function only support for DHT10");
      return 0;
  }
  
}



/*****************************************************************************/
/*****************************************************************************/

int DHT::i2cReadByte(uint8_t& byte)
{
	int cnt = 0;
	Wire.requestFrom(DEFAULT_IIC_ADDR,1);
	while(1 != Wire.available()){
		cnt++;
		if(cnt >= 10 )
			return -1;
		delay(1);
	}

	byte = Wire.read();
	return 0;
}

int DHT::i2cReadBytes(uint8_t *bytes,uint32_t len)
{
	int cnt = 0;
	Wire.requestFrom(DEFAULT_IIC_ADDR,len);
	while(len != (uint32_t) Wire.available()){
		cnt++;
		if(cnt >= 10 )
			return -1;
		delay(1);
	}
	for(uint32_t i=0;i<len;i++){
		bytes[i] = Wire.read();
	}
	return 0;
}


int DHT::i2cWriteBytes(uint8_t *bytes,uint32_t len)
{
	Wire.beginTransmission(DEFAULT_IIC_ADDR);
	for(uint32_t i=0;i< len;i++)
	{
		Wire.write(bytes[i]);
	}
	return Wire.endTransmission();
}

int DHT::i2cWriteByte(uint8_t byte)
{
	Wire.beginTransmission(DEFAULT_IIC_ADDR);
	Wire.write(byte);
	return Wire.endTransmission();
}




